﻿// 
//    Copyright 2010 The JaernCloud Project
// 
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
// 
//        http://www.apache.org/licenses/LICENSE-2.0
// 
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JaernCloud.Language.Compiler.Types.Builtin.Model;
using JaernCloud.Language.Compiler.Types;
using System.Dynamic;

namespace JaernCloud.Language.Runtime.Model {

    /// <summary>
    /// Node object representing an entity model
    /// </summary>
    public class ModelNodeObject : DynamicObject, INodeObject<ModelNodeType>, IScopedNodeObject<ModelNodeType> {

        readonly int _id = NodeObjectExtensions.NewId;
        public int Id { get { return _id; } }

        public INodeObject<ModelNodeType> FromType(INodeType nodeType, INodeObjectGenerator nodeObjectGenerator) {
            return new ModelNodeObject { _type = (ModelNodeType)nodeType, _nodeObjectGenerator = nodeObjectGenerator };
        }

        #region DynamicObject

        public override bool TryConvert(System.Dynamic.ConvertBinder binder, out object result) {
            return base.TryConvert(binder, out result);
        }

        public override bool TrySetMember(SetMemberBinder binder, object value) {
            foreach (var child in Children) {
                if (child.Name == binder.Name && child is DynamicObject) {
                    DynamicObject dynObj = child as DynamicObject;
                    if (dynObj.TrySetMember(binder, value)) {
                        return true;
                    }
                }
            }
            return base.TrySetMember(binder, value);
        }

        #endregion


        #region model entity properties

        public string ScopeNodeObjectId {
            get {
                StringBuilder sb = new StringBuilder(Type.ScopeNodeId);
                if (!String.IsNullOrEmpty(EntityId)) {
                    // TODO: Figure out how to handle persisted nodes and give them scope identity
                    sb.Append('/');
                    sb.Append(Id);
                }
                return sb.ToString();
            }
        }
        /// <summary>
        /// Should be set by the persistence provider when the nodeObject is retrieved
        /// </summary>
        public string EntityId { get; set; }

        /// <summary>
        /// Version of the persisted type
        /// </summary>
        public int EntityVersion { get; set; }

        #endregion

        // node object
        private INodeObject<INodeType> _parent;
        private ModelNodeType _type;
        private IEnumerable<INodeObject<INodeType>> _children;
        private INodeObjectGenerator _nodeObjectGenerator;

        public ModelNodeType Type {
            get { return _type; }
        }

        public IEnumerable<INodeObject<INodeType>> Children {
            get {
                if (_children == null) {
                    var children = new List<INodeObject<INodeType>>();
                    foreach (INodeType nodeType in _type.Children) {
                        var child = _nodeObjectGenerator.GetNodeObjectInstanceFor(nodeType);
                        child.Parent = this as INodeObject<INodeType>;
                        children.Add(child);
                    }

                    _children = children;
                }
                return _children;
            }
        }

        public INodeObject<INodeType> Parent {
            get {
                return _parent;
            }
            set {
                _parent = value;
            }
        }

        public string Name {
            get { return _type.Name; }
        }

        public IEnumerable<string> Names {
            get { return _type.Names; }
        }

        public IEnumerable<object> Values {
            get { yield break; }
        }

        public override string ToString() {
            return "[" + Id + "] " + this.GetType().Name + "(" + _type.ToString() + ")";
        }

    }
}
